#include <assert.h>
#include "leptjson_v1.hpp"
#include <math.h>
#include <errno.h>


std::unique_ptr<ILeptJSON> create_error_object(const lept_error_type _lept_error){
    return std::unique_ptr<ILeptJSON>(new LeptError(_lept_error));
}

/* MultiManageLeptJSON */
void MultiManageLeptJSON::lept_parse_whitespace(lept_context& c){
    const char *p =c.json;
    while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
        p++;
    c.json = p;
}


std::unique_ptr<ILeptJSON> MultiManageLeptJSON::lept_parse_literal(lept_context& c,std::string const& str){
    assert(c.json[0]==str[0]);
    for(int i=1;i<str.size();i++){
        if(c.json[i]!=str[i])
            return create_error_object(lept_error_type::LEPT_PARSE_INVALID_VALUE);
    }
    c.json+=str.size();
    if(str=="null")
        return  std::unique_ptr<ILeptJSON>(new LeptNull());
    else if(str=="false")
        return  std::unique_ptr<ILeptJSON>(new LeptFalse());
    else if(str=="true")
        return  std::unique_ptr<ILeptJSON>(new LeptTrue());
    else
        return create_error_object(lept_error_type::LEPT_PARSE_INVALID_VALUE);
}



#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')
#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')
std::unique_ptr<ILeptJSON> MultiManageLeptJSON::lept_parse_number(lept_context& c){
    const char* p = c.json;
    if (*p == '-') ++p;
    if (*p == '0') ++p;
    else {
        if (!ISDIGIT1TO9(*p)) return create_error_object(lept_error_type::LEPT_PARSE_INVALID_VALUE);
        for (p++; ISDIGIT(*p); p++);
    }
    if (*p == '.') {
        p++;
        if (!ISDIGIT(*p)) return create_error_object(lept_error_type::LEPT_PARSE_INVALID_VALUE);
        for (p++; ISDIGIT(*p); p++);
    }
    if (*p == 'e' || *p == 'E') {
        p++;
        if (*p == '+' || *p == '-') p++;
        if (!ISDIGIT(*p)) return create_error_object(lept_error_type::LEPT_PARSE_INVALID_VALUE);
        for (p++; ISDIGIT(*p); p++);
    }
    errno = 0;
    std::unique_ptr<ILeptJSON> ret=std::unique_ptr<ILeptJSON>(new LeptNumber(strtod(c.json,NULL)));
    if (errno == ERANGE && (ret->lept_get_element<double>() == HUGE_VAL || ret->lept_get_element<double>() == -HUGE_VAL))
        return create_error_object(lept_error_type::LEPT_PARSE_NUMBER_TOO_BIG);
    c.json = p;
    return ret;
}

const char* lept_parse_hex4(const char* p,unsigned& u){
    u=0;
    for(int i=0;i<4;i++){
        char ch=*p++;
        u<<=4;
        if(ch>='0' && ch <='9') u+=ch-'0';
        else if(ch>='a' && ch <='f') u+=ch-'a'+10;
        else if(ch>='A' && ch <='F') u+=ch-'A'+10;
        else    return NULL;
    }
    return p;
}

void lept_encode_utf8(std::string& buffer,unsigned u){
    if(u<=0x007F){
        buffer+=u & 0xFF;
    }
    else if(u<=0x07FF){  
        buffer+=0xC0 | ((u>>6) & 0xFF);
        buffer+=0x80 | (u & 0x3F);
    }
    else if(u<=0xFFFF){
        buffer+=0xE0 | ((u>>12) & 0xFF);
        buffer+=0x80 | ((u>>6) & 0x3F);
        buffer+=0x80 | (u & 0x3F);
    }
    else{
        assert(u<=0x10FFFF);
        buffer+=0xF0 | ((u>>18) & 0xFF);
        buffer+=0x80 | ((u>>12) & 0x3F);
        buffer+=0x80 | ((u>>6) & 0x3F);
        buffer+=0x80 | (u & 0x3F);
    }
}


#define ISLOWDELEGATE(n) ((n) >= 0xDC00 && (n) <= 0xDFFF )
#define ISHIGHDELEGATE(n) ((n) >= 0xD800 && (n) <= 0xDBFF )
std::unique_ptr<ILeptJSON> MultiManageLeptJSON::lept_parse_string(lept_context& c){
    assert(*c.json=='\"');
    const char* p=c.json;
    ++p;
    unsigned u=0,r=0;
    std::string buffer;
    for(;;){
        char ch=*p++;
        switch (ch) {
            case '\"':
                c.json=p;
                return std::unique_ptr<ILeptJSON>(new LeptString(buffer));
            case '\\':
                 switch (*p++) {
                    case '\"': buffer.push_back('\"'); break;
                    case '\\': buffer.push_back('\\'); break;
                    case '/':  buffer.push_back('/'); break;
                    case 'b':  buffer.push_back('\b'); break;
                    case 'f':  buffer.push_back('\f'); break;
                    case 'n':  buffer.push_back('\n'); break;
                    case 'r':  buffer.push_back('\r'); break;
                    case 't':  buffer.push_back('\t'); break;
                    case 'u':
                        if (!(p = lept_parse_hex4(p, u)))
                            return create_error_object(lept_error_type::LEPT_PARSE_INVALID_UNICODE_HEX);
                        if(ISHIGHDELEGATE(u)) {
                        /*surrogate handling */
                            if (*p++ != '\\')
                                return create_error_object(lept_error_type::LEPT_PARSE_INVALID_UNICODE_SURROGATE);
                            if (*p++ != 'u')
                                return create_error_object(lept_error_type::LEPT_PARSE_INVALID_UNICODE_SURROGATE);
                            if(!(p = lept_parse_hex4(p, r)))
                                return create_error_object(lept_error_type::LEPT_PARSE_INVALID_UNICODE_HEX);
                            if(!ISLOWDELEGATE(r))
                                return create_error_object(lept_error_type::LEPT_PARSE_INVALID_UNICODE_SURROGATE);
                            u=0x10000 + ((u - 0xD800) <<10) | (r - 0xDC00);
                        }
                        lept_encode_utf8(buffer, u);
                        break;
                    default:
                        return create_error_object(lept_error_type::LEPT_PARSE_INVALID_STRING_ESCAPE);
                }
                break;
            case '\0':
                return create_error_object(lept_error_type::LEPT_PARSE_MISS_QUOTATION_MARK);
                break;
            default:
                if((unsigned char)ch < 0x20)
                    return create_error_object(lept_error_type::LEPT_PARSE_INVALID_STRING_CHAR);
                buffer.push_back(ch);
        }
    }
}

std::unique_ptr<ILeptJSON> MultiManageLeptJSON::lept_parse_array(lept_context& c){
    /* array = %x5B ws [ value *( ws %x2C ws value ) ] ws %x5D */
    assert(*c.json=='[');
    ++c.json;
    std::vector<std::unique_ptr<ILeptJSON>> v;
    while(1){
        lept_parse_whitespace(c);
        std::unique_ptr<ILeptJSON> curLept=lept_parse_value(c);
        lept_parse_whitespace(c);
        if(curLept->lept_get_error_type()==lept_error_type::LEPT_PARSE_OK)
            v.emplace_back(std::move(curLept));
        if(*c.json==',') {
            ++c.json;
        }
        else if(*c.json==']'){
            ++c.json;
            return std::unique_ptr<ILeptJSON>(new LeptArray(v));
        }
        else{
            return create_error_object(lept_error_type::LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET);
        }
    }  
}

std::unique_ptr<ILeptJSON> MultiManageLeptJSON::lept_parse_object(lept_context& c){
    assert(*c.json=='{');
    ++c.json;
    std::string _key;
    std::unique_ptr<ILeptJSON> _val;
    std::unique_ptr<ILeptJSON> ret=std::unique_ptr<ILeptJSON>(new LeptObject);
    while(1){
        /* get key */
        lept_parse_whitespace(c);
        if(*c.json!='\"' && *c.json!='}'){
            return create_error_object(lept_error_type::LEPT_PARSE_MISS_KEY);
        }
        std::unique_ptr<ILeptJSON> curLept=lept_parse_value(c);
        if(curLept->lept_get_error_type()==lept_error_type::LEPT_PARSE_OK)
            _key=curLept->lept_get_element<std::string>();
        lept_parse_whitespace(c);
        if(*c.json!=':' && !_key.empty()) {
            return create_error_object(lept_error_type::LEPT_PARSE_MISS_COLON);
        }
        if(*c.json==':'){
            ++c.json;
        }
        /*get value */
        lept_parse_whitespace(c);
        std::unique_ptr<ILeptJSON> _val=lept_parse_value(c);
        lept_parse_whitespace(c);
        if(*c.json==','){
            ret->lept_set_element<std::unordered_map<std::string,std::unique_ptr<ILeptJSON>>>().insert(std::make_pair(std::move(_key),std::move(_val)));
            ++c.json;
        }
        else if(*c.json=='}'){
            if(!_key.empty())
                ret->lept_set_element<std::unordered_map<std::string,std::unique_ptr<ILeptJSON>>>().insert(std::make_pair(std::move(_key),std::move(_val)));
            ++c.json;
            return ret;
        }
        else{
            return create_error_object(lept_error_type::LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET);
        }

    }
}
std::unique_ptr<ILeptJSON> MultiManageLeptJSON::lept_parse_value(lept_context& c){
    switch (*c.json){
        case 't':  return lept_parse_literal(c, "true");
        case 'f':  return lept_parse_literal(c, "false");
        case 'n':  return lept_parse_literal(c, "null");
        case '\"': return lept_parse_string(c);
        case '[':   return lept_parse_array(c);
        case '{': return lept_parse_object(c);
        case '\0': return create_error_object(lept_error_type::LEPT_PARSE_EXPECT_VALUE);
        default:    return lept_parse_number(c);
    }
}

std::unique_ptr<ILeptJSON> MultiManageLeptJSON::lept_parse(const char* json){
    lept_context c;
    c.json=json;
    lept_parse_whitespace(c);
    std::unique_ptr<ILeptJSON> ret = lept_parse_value(c);
    if(ret->lept_get_error_type()==lept_error_type::LEPT_PARSE_OK){
        lept_parse_whitespace(c);
        if(*c.json!='\0'){
            /* 重设指针 */
            ret.reset(new LeptError(lept_error_type::LEPT_PARSE_ROOT_NOT_SINGULAR));
        }
    }
    return std::move(ret);
}